package com.auto.nio.groupchat;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

public class GroupServer {
    private Selector selector;
    private ServerSocketChannel listenChannel;
    private static final int PORT = 6667;

    //初始化成员变量
    public GroupServer() {
        try {
            //选择器
            selector = Selector.open();
            //通道
            listenChannel = ServerSocketChannel.open();
            //绑定端口号
            listenChannel.socket().bind(new InetSocketAddress(PORT));
            //设置为非阻塞
            listenChannel.configureBlocking(false);
            listenChannel.register(selector, SelectionKey.OP_ACCEPT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 监听方法
     */
    public void listen() {
        try {
            while (true) {
                int count = selector.select();
                if (count > 0) {//有连接
                    //获取SelectionKey集合
                    Set<SelectionKey> selectionKeys = selector.selectedKeys();
                    Iterator<SelectionKey> keyIterator = selectionKeys.iterator();
                    while (keyIterator.hasNext()) {
                        SelectionKey selectionKey = keyIterator.next();
                        //如果是连接事件
                        if (selectionKey.isAcceptable()) {
                            //获取SocketChannel --> Socket
                            SocketChannel socketChannel = listenChannel.accept();
                            //设置为非阻塞
                            socketChannel.configureBlocking(false);
                            //注册
                            socketChannel.register(selector, SelectionKey.OP_READ);
                            System.out.println(socketChannel.getRemoteAddress() + " 上线了");
                        }

                        //如果是读事件，通道发生了可读事件
                        if (selectionKey.isReadable()) {
                            //执行读取数据的业务逻辑
                            readData(selectionKey);
                        }
                        //手动删除selectionKey，以防止重复操作
                        keyIterator.remove();
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 读取客户端消息
     *
     * @param selectionKey
     */
    private void readData(SelectionKey selectionKey) {
        SocketChannel socketChannel = null;
        try {
            //获取客户端SocketChannel --> Socket
            socketChannel = (SocketChannel) selectionKey.channel();
            //创建缓冲区
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            //将客户端数据从通道读取到缓冲区中
            socketChannel.read(byteBuffer);
            String msg = new String(byteBuffer.array());
            System.out.println("来自客户端：" + msg);
            //转发到其他的客户端
            sendToOtherClients(msg, socketChannel);
        } catch (IOException e) {
            //读取数据过程中发生异常，表示其他客户端关闭了抛出的异常
            try {
                System.out.println(socketChannel.getRemoteAddress() + "下线了");
                //取消注册
                selectionKey.cancel();
                //关闭通道
                socketChannel.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }

    /**
     * 将消息转发给其他客户端
     *
     * @param msg         转发的消息
     * @param selfChannel 自身当前的通道
     */
    private void sendToOtherClients(String msg, SocketChannel selfChannel) throws IOException {
        System.out.println("服务器转发消息...");
        //遍历当前所有的注册到Selector的SocketChannel
        for (SelectionKey selectionKey : selector.keys()) {
            //获取通道
            Channel targetChannel = selectionKey.channel();
            //判断通道类型是否为SocketChannel，并且不是当前通道
            if (targetChannel instanceof SocketChannel && targetChannel != selfChannel) {
                //获取到转发消息通道
                SocketChannel destChannel = (SocketChannel) targetChannel;
                //根据消息生成对应的ByteBuffer，将消息数据存入缓冲区
                ByteBuffer byteBuffer = ByteBuffer.wrap(msg.getBytes());
                //将缓冲区数据写入通道
                destChannel.write(byteBuffer);
            }
        }
    }

    public static void main(String[] args) {
        GroupServer groupServer = new GroupServer();
        groupServer.listen();
    }
}
